package com.yzs.exam.config.spring.security;


import com.yzs.exam.config.property.CookieConfig;
import com.yzs.exam.utils.JsonUtil;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * 被该过滤器拦截 -- 并进行身份验证
 * 登录参数序列化
 * @author yzs
 * @create 2021-03-20 21:16
 */
public class RestLoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    //日志对象
    private final org.slf4j.Logger logger = LoggerFactory.getLogger(RestLoginAuthenticationFilter.class);

    //
    protected RestLoginAuthenticationFilter() {
        //defaultFilterProcessesUrl -- 默认提交的路径是/login 提交方式为post
        super(new AntPathRequestMatcher("/api/user/login", "POST"));
    }

    //封装为认证的用户信息
    @Override
    public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException {
        //Authentication的实现类
        //该类有两个构造器，
        //一个用于封装前端请求传入的未认证的用户信息
        //一个用于封装认证成功后的用户信息
        //getParameter获取的是表单的元素（postget传递的参数值）或者url的参数值，返回String
        //getAttribute获取的是页面自己定义的对象进行操作，返回Object
        //本设计采用前后端分离，所以前端发送的是json数据格式，如果去getParameter得到的值为空，我们就直接从请求体
        //中拿到json数据进行转化为认证对象
        //request.getInputStream第二次获取时为空
        UsernamePasswordAuthenticationToken authRequest;

        try (InputStream is = httpServletRequest.getInputStream()){
            logger.info("登录请求来了!");
            AuthenticationBean authenticationBean = JsonUtil.toJsonObject(is, AuthenticationBean.class);
            //实现自动登录 -- 通过Base-64编码的cookie识别以前记住的用户
            httpServletRequest.setAttribute(TokenBasedRememberMeServices.DEFAULT_PARAMETER,authenticationBean.isRemember());
            //一个用于封装认证成功后的用户信息
            authRequest = new UsernamePasswordAuthenticationToken(authenticationBean.getUserName(), authenticationBean.getPassword());
            logger.info("封装用户信息：" + authenticationBean.getUserName());
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
            //一个用于封装前端请求传入的未认证的用户信息
            authRequest = new UsernamePasswordAuthenticationToken("", "");
        }

        setDetails(httpServletRequest, authRequest);
        //返回一个已认证的对象Authentication
        return this.getAuthenticationManager().authenticate(authRequest);
    }

    //定义记住我功能，指定将要使用的 UserDetailsService
    void setUserDetailsService(UserDetailsService userDetailsService) {
        RestTokenBasedRememberMeServices tokenBasedRememberMeServices =
                new RestTokenBasedRememberMeServices(CookieConfig.getName(), userDetailsService);
        tokenBasedRememberMeServices.setTokenValiditySeconds(CookieConfig.getInterval());
        setRememberMeServices(tokenBasedRememberMeServices);//设置自定义的记住我功能
    }

    /**
     *  认证信息设置到认证详情存储源中
     *  对用户的登录信息的详细信息进行填充
     * @param request
     * @param authRequest
     */
    private void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }
}
